package command

import (
	"time"
	"os/exec"
	"bytes"
	"github.com/pkg/errors"
	"fmt"

	"strings"
	"sync"
	"io"
)

const (
	ANSIBALE_RUN_TIMEOUT = 30
)

type Command struct {
	CmdLine string							`json:"cmdLine"`
	Timeout time.Duration
	TerminateChan chan struct{}
	Setpgid bool
	command *exec.Cmd
	stdout  bytes.Buffer
	stderr  bytes.Buffer
	//StdFile *os.File
}

func NewCmd(c *Command) (*Command, error) {
	if c.Timeout == 0 * time.Second {
		c.Timeout = ANSIBALE_RUN_TIMEOUT * time.Second
	}
	if c.TerminateChan == nil {
		c.TerminateChan = make(chan struct{})
	}
	cmd := exec.Command("/bin/bash", "-c", c.CmdLine)
	if c.Setpgid {
		//cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	}
	cmd.Stderr = &c.stderr
	cmd.Stdout = &c.stdout
	c.command = cmd

	return c, nil
}

//func (c *Command) terminate() error {
//	if c.Setpgid {
//		return syscall.Kill(-c.command.Process.Pid, syscall.SIGKILL)
//
//	} else {
//		return syscall.Kill(c.command.Process.Pid, syscall.SIGKILL)
//	}
//}

func (c *Command) Run(wg *sync.WaitGroup , outChan chan string)  {
	defer wg.Done()

	if err := c.command.Start(); err != nil {
		outChan <- fmt.Sprintf("%s\n",err.Error())
		return
	}
	errChan := make(chan error)
	go func() {
		errChan <- c.command.Wait()
		defer close(errChan)
	}()


	var err error
	select {
	case err = <- errChan:
		outChan <- fmt.Sprintf("%s\n%s\n",c.Stdout(),c.Stderr())

	case <-time.After(c.Timeout):
			err = errors.New(fmt.Sprintf("cmd run timeout, cmd [%s], time[%v]", c.CmdLine, c.Timeout))
		    outChan <- fmt.Sprintf("%s\n",err.Error())

	case <-c.TerminateChan:
		//err = c.terminate()
		//if err == nil {
		//	err = errors.New(fmt.Sprintf("cmd is terminated, cmd [%s]", c.CmdLine))
		//}
		err = errors.New(fmt.Sprintf("cmd is terminated, cmd [%s]", c.CmdLine))
		outChan <- fmt.Sprintf("%s\n",err.Error())
	}

}

func (c *Command) RunOnce() error {
	if err := c.command.Start(); err != nil {
		return err
	}

	errChan := make(chan error)
	go func(){
		errChan <- c.command.Wait()
		defer close(errChan)
	}()

	var err error
	select {
	case err = <-errChan:
	case <-time.After(c.Timeout):
			err = errors.New(fmt.Sprintf("cmd run timeout, cmd [%s], time[%v]", c.CmdLine, c.Timeout))
	case <-c.TerminateChan:
			err = errors.New(fmt.Sprintf("cmd is terminated, cmd [%s]", c.CmdLine))
	}
	return err
}


func (c *Command) SetDir(workDir string)  {
	c.command.Dir = workDir
}

func (c *Command) SetCmd(workDir string,cmdline string)  {

	c.command =  exec.Command("/bin/bash", "-c", cmdline)
	c.command.Dir = workDir
}

func (c *Command) SetGitCmd(workDir string,cmdline string)  {
	cmdParam := strings.Split(cmdline," ")
	c.command = exec.Command("git",cmdParam...) //跨平台
	//c.command =  exec.Command("/bin/bash", "-c", cmdline)
	c.command.Dir = workDir
}


func (c *Command) Stderr() string {
	return strings.TrimSpace(string(c.stderr.Bytes()))
}

func (c *Command) Stdout() string {
	return strings.TrimSpace(string(c.stdout.Bytes()))
}

//func (c *Command) SetStdToFile(f *os.File)  {
//	c.command.Stdout = f
//	c.command.Stderr = f
//}

func (c *Command) SetStd(w io.Writer)  {
	c.command.Stdout = w
	c.command.Stderr = w
}